package com.aaaa.scheduler;

import static java.lang.Math.max;

import com.google.ortools.Loader;
import com.google.ortools.sat.*;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;

/** Minimal Jobshop problem. */
public class Hello {
    public static void main(String[] args) throws IOException {
        Loader.loadNativeLibraries();

        /**
         * 功能描述：定义数据
         *
         */
        //工单
        class Task {
            int machine;
            int duration;
            Task(int machine, int duration) {
                this.machine = machine;
                this.duration = duration;
            }
        }

        //一个示例
        List<List<Task>> allJobs = new ArrayList<>();
//        List<List<Task>> allJobs =
//                Arrays.asList(Arrays.asList(new Task(0, 3), new Task(1, 3), new Task(2, 2)), // Job0
//                        Arrays.asList(new Task(0, 1), new Task(2, 5), new Task(1, 3)), // Job1
//                        Arrays.asList(new Task(1, 3), new Task(0, 2), new Task(2, 3)) // Job2
//                );
        List<List<Task>> allJobs1 =
                Arrays.asList(Arrays.asList(new Task(0, 3), new Task(1, 2), new Task(2, 2)), // Job0
                        Arrays.asList(new Task(0, 2), new Task(2, 1), new Task(1, 4)), // Job1
                        Arrays.asList(new Task(1, 4), new Task(2, 3)) // Job2
                );


        FileReader fileReader = new FileReader("D:\\Java\\jsp.txt");
        BufferedReader bufferedReader =new BufferedReader(fileReader);
        String buf = "";
        boolean firstflag = true;
        while((buf = bufferedReader.readLine()) != null){
            System.out.println(buf);
            if(firstflag){//第一行：订单数、设备数
                firstflag = false;
            }else{//其他行：订单的每道工序和生产时间
                int productProcessNum = buf.charAt(0)-'0';//当前订单的工序数
                List<Task> oplist = new ArrayList<>();
                for(int i=2; i<=productProcessNum * 4; i+=4){
                    int machinen = buf.charAt(i)-'0'-1, time = buf.charAt(i+2)-'0';
                    oplist.add(new Task(machinen,time));
                }
                allJobs.add(oplist);
            }
        }
//        for (List<Task> job : allJobs) {
//            for (Task task : job) {
//                System.out.println(task.machine+":"+task.duration);
//            }
//        }
        //numMachines为机器数量
        int numMachines = 1;
        for (List<Task> job : allJobs) {
            for (Task task : job) {
                numMachines = max(numMachines, 1 + task.machine);
            }
        }
        final int[] allMachines = IntStream.range(0, numMachines).toArray();//所有的机器的集合
        // horizon作为所有持续时间的总和
        int horizon = 0;
        for (List<Task> job : allJobs) {
            for (Task task : job) {
                horizon += task.duration;
            }
        }

        /**
         * 功能描述：创建模型
         *
         */
        CpModel model = new CpModel();

        class TaskType {
            IntVar start;
            IntVar end;
            IntervalVar interval;
        }
        Map<List<Integer>, TaskType> allTasks = new HashMap<>();
        Map<Integer, List<IntervalVar>> machineToIntervals = new HashMap<>();

        for (int jobID = 0; jobID < allJobs.size(); ++jobID) {
            List<Task> job = allJobs.get(jobID);
            for (int taskID = 0; taskID < job.size(); ++taskID) {
                Task task = job.get(taskID);
                String suffix = "_" + jobID + "_" + taskID;

                TaskType taskType = new TaskType();
                taskType.start = model.newIntVar(0, horizon, "start" + suffix);
                taskType.end = model.newIntVar(0, horizon, "end" + suffix);
                taskType.interval = model.newIntervalVar(
                        taskType.start, task.duration, taskType.end, "interval" + suffix);

                List<Integer> key = Arrays.asList(jobID, taskID);
                allTasks.put(key, taskType);
                machineToIntervals.computeIfAbsent(task.machine, (Integer k) -> new ArrayList<>());
                machineToIntervals.get(task.machine).add(taskType.interval);
            }
        }
        /**
         * 功能描述：定义约束
         *
         */
        // 创建无重叠约束，从而防止同一台机器的任务在时间上重叠。
        for (int machine : allMachines) {
            List<IntervalVar> list = machineToIntervals.get(machine);
            model.addNoOverlap(list.toArray(new IntervalVar[0]));
        }

        // 添加优先约束，防止同一作业的连续任务在时间上重叠
        for (int jobID = 0; jobID < allJobs.size(); ++jobID) {
            List<Task> job = allJobs.get(jobID);
            for (int taskID = 0; taskID < job.size() - 1; ++taskID) {
                List<Integer> prevKey = Arrays.asList(jobID, taskID);
                List<Integer> nextKey = Arrays.asList(jobID, taskID + 1);
                model.addGreaterOrEqual(allTasks.get(nextKey).start, allTasks.get(prevKey).end);
            }
        }
        /**
         * 功能描述：定义目标
         *
         */
        // 创建一个变量，objVar其值为所有作业的最大结束时间
        IntVar objVar = model.newIntVar(0, horizon, "makespan");
        List<IntVar> ends = new ArrayList<>();
        for (int jobID = 0; jobID < allJobs.size(); ++jobID) {
            List<Task> job = allJobs.get(jobID);
            List<Integer> key = Arrays.asList(jobID, job.size() - 1);
            ends.add(allTasks.get(key).end);
        }
        model.addMaxEquality(objVar, ends.toArray(new IntVar[0]));
        model.minimize(objVar);

        /**
         * 功能描述：调用求解器
         *
         */
        // Creates a solver and solves the model.
        CpSolver solver = new CpSolver();
        CpSolverStatus status = solver.solve(model);
        /**
         * 功能描述：显示结果
         *
         */
        if (status == CpSolverStatus.OPTIMAL || status == CpSolverStatus.FEASIBLE) {
            class AssignedTask {
                int jobID;
                int taskID;
                int start;
                int duration;
                // Ctor
                AssignedTask(int jobID, int taskID, int start, int duration) {
                    this.jobID = jobID;
                    this.taskID = taskID;
                    this.start = start;
                    this.duration = duration;
                }
            }
            class SortTasks implements Comparator<AssignedTask> {
                @Override
                public int compare(AssignedTask a, AssignedTask b) {
                    if (a.start != b.start) {
                        return a.start - b.start;
                    } else {
                        return a.duration - b.duration;
                    }
                }
            }
            System.out.println("Solution:");
            // Create one list of assigned tasks per machine.
            Map<Integer, List<AssignedTask>> assignedJobs = new HashMap<>();
            for (int jobID = 0; jobID < allJobs.size(); ++jobID) {
                List<Task> job = allJobs.get(jobID);
                for (int taskID = 0; taskID < job.size(); ++taskID) {
                    Task task = job.get(taskID);
                    List<Integer> key = Arrays.asList(jobID, taskID);
                    AssignedTask assignedTask = new AssignedTask(
                            jobID, taskID, (int) solver.value(allTasks.get(key).start), task.duration);
                    assignedJobs.computeIfAbsent(task.machine, (Integer k) -> new ArrayList<>());
                    assignedJobs.get(task.machine).add(assignedTask);
                }
            }

            // Create per machine output lines.
            String output = "";
            for (int machine : allMachines) {
                // Sort by starting time.
                Collections.sort(assignedJobs.get(machine), new SortTasks());
                String solLineTasks = "Machine " + machine + ": ";
                String solLine = "           ";

                for (AssignedTask assignedTask : assignedJobs.get(machine)) {
                    String name = "job_" + assignedTask.jobID + "_task_" + assignedTask.taskID;
                    // Add spaces to output to align columns.
                    solLineTasks += String.format("%-15s", name);

                    String solTmp =
                            "[" + assignedTask.start + "," + (assignedTask.start + assignedTask.duration) + "]";
                    // Add spaces to output to align columns.
                    solLine += String.format("%-15s", solTmp);
                }
                output += solLineTasks + "%n";
                output += solLine + "%n";
            }
            System.out.printf("Optimal Schedule Length: %f%n", solver.objectiveValue());
            System.out.printf(output);
        } else {
            System.out.println("No solution found.");
        }

        // Statistics.
        System.out.println("Statistics");
        System.out.printf("  conflicts: %d%n", solver.numConflicts());
        System.out.printf("  branches : %d%n", solver.numBranches());
        System.out.printf("  wall time: %f s%n", solver.wallTime());
    }

}
